// Lambda 表达式:
// - [] 空捕获列表
// - [name1, name2, ...] 捕获一系列变量
// - [&] 引用捕获, 让编译器自行推导引用列表
// - [=] 值捕获, 让编译器自行推导值捕获列表
//
// ```cpp
// [捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 {
//     // 函数体
// }
// ```

#include <iostream>
#include <memory>


// 值捕获: 与参数传值类似，值捕获的前提是变量可以拷贝，不同之处则在于，
// 被捕获的变量在 Lambda 表达式被创建时拷贝，而非调用时才拷贝：
void lambda_value_capture() {
    int value = 1;
    auto copy_value = [value] {
        return value;
    };
    value = 100;
    auto stored_value = copy_value();
    std::cout << "stored_value = " << stored_value << std::endl;
    // 这时, stored_value == 1, 而 value == 100.
    // 因为 copy_value 在创建时就保存了一份 value 的拷贝
}

// 引用捕获: 与引用传参类似，引用捕获保存的是引用，值会发生变化。
void lambda_reference_capture() {
    int value = 1;
    auto copy_value = [&value] {
        return value;
    };
    value = 100;
    auto stored_value = copy_value();
    std::cout << "stored_value = " << stored_value << std::endl;
    // 这时, stored_value == 100, value == 100.
    // 因为 copy_value 保存的是引用
}

// 隐式捕获: 手动书写捕获列表有时候是非常复杂的，这种机械性的工作可以交给编译器来处理，
// 这时候可以在捕获列表中写一个 & 或 = 向编译器声明采用引用捕获或者值捕获

// 隐式值捕获
void implicit_lambda_value_capture() {
    int value = 1;
    auto copy_value = [=] {
        return value;
    };
    value = 100;
    auto stored_value = copy_value();
    std::cout << "stored_value = " << stored_value << std::endl;
}

// 隐式引用捕获
void implicit_lambda_reference_capture() {
    int value = 1;
    auto copy_value = [&] {
        return value;
    };
    value = 100;
    auto stored_value = copy_value();
    std::cout << "stored_value = " << stored_value << std::endl;
}

// 表达式捕获
void lambda_expression_capture() {
    auto important = std::make_unique<int>(1);
    auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
        return x + y + v1 + (*v2);
    };
    std::cout << add(3, 4) << std::endl;
}

void generic_lambda() {
    auto add = [](auto x, auto y) {
        return x + y;
    };

    std::cout << add(1, 2) << std::endl;
    std::cout << add(1.1, 2.2) << std::endl;
}

int main() {
    lambda_value_capture();
    lambda_reference_capture();
    implicit_lambda_value_capture();
    implicit_lambda_reference_capture();
    lambda_expression_capture();
    generic_lambda();
    return 0;
}